<!DOCTYPE html>
<html>
	<head>
		<title>three.js css3d - mixed</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			body {
				background-color: #ffffff;
			}
		</style>
	</head>
	<body>
		<script type="importmap">
			{
				"imports": {
					"three": "../build/three.module.js",
					"three/addons/": "./jsm/"
				}
			}
		</script>

		<script type="module">

			import * as THREE from 'three';

			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
			import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';

			let camera, scene, rendererCSS3D, rendererWebGL;
			let controls;

			init();

			function init() {

				const controlsDomElement = document.createElement( 'div' );
				controlsDomElement.style.position = 'absolute';
				controlsDomElement.style.top = '0';
				controlsDomElement.style.width = '100%';
				controlsDomElement.style.height = '100%';
				document.body.appendChild( controlsDomElement );

				rendererCSS3D = new CSS3DRenderer();
				rendererCSS3D.setSize( window.innerWidth, window.innerHeight );
				document.body.appendChild( rendererCSS3D.domElement );

				rendererWebGL = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
				rendererWebGL.domElement.style.position = 'absolute';
				rendererWebGL.domElement.style.top = '0';
				rendererWebGL.domElement.style.pointerEvents = 'none';
				rendererWebGL.setPixelRatio( window.devicePixelRatio );
				rendererWebGL.setSize( window.innerWidth, window.innerHeight );
				rendererWebGL.toneMapping = THREE.NeutralToneMapping;
				rendererWebGL.setAnimationLoop( animate );
				document.body.appendChild( rendererWebGL.domElement );

				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10000 );
				camera.position.set( - 1000, 500, 1500 );

				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0xf0f0f0 );

				// Add room
				const roomGeometry = new THREE.EdgesGeometry( new THREE.BoxGeometry( 4000, 2000, 4000, 10, 5, 10 ) );
				const roomMaterial = new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2, transparent: true } );
				const room = new THREE.LineSegments( roomGeometry, roomMaterial );
				scene.add( room );

				// Add light
				const hemisphereLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 4 );
				hemisphereLight.position.set( - 25, 100, 50 );
				scene.add( hemisphereLight );

				// Add cutout mesh
				const geometry = new THREE.PlaneGeometry( 1024, 768 );
				const material = new THREE.MeshBasicMaterial( {
					color: 0xff0000,
					blending: THREE.NoBlending,
					opacity: 0,
					premultipliedAlpha: true
				} );
				const mesh = new THREE.Mesh( geometry, material );
				scene.add( mesh );

				// Add frame
				const frame = buildFrame( 1024, 768, 50 );
				scene.add( frame );

				// Add CSS3D element
				const iframe = document.createElement( 'iframe' );
				iframe.style.width = '1028px';
				iframe.style.height = '768px';
				iframe.style.border = '0px';
				iframe.style.backfaceVisibility = 'hidden';
				iframe.src = './#webgl_animation_keyframes';
				scene.add( new CSS3DObject( iframe ) );

				// Add controls
				controls = new OrbitControls( camera );
				controls.connect( controlsDomElement );
				controls.addEventListener( 'start', () => iframe.style.pointerEvents = 'none' );
				controls.addEventListener( 'end', () => iframe.style.pointerEvents = 'auto' );
				controls.enableDamping = true;

				window.addEventListener( 'resize', onWindowResize );

			}

			function buildFrame( width, height, thickness ) {

				const group = new THREE.Group();
				const material = new THREE.MeshStandardMaterial( { color: 0x2200ff } );

				// Create the frame border
				const outerShape = new THREE.Shape();
				outerShape.moveTo( - ( width / 2 + thickness ), - ( height / 2 + thickness ) );
				outerShape.lineTo( width / 2 + thickness, - ( height / 2 + thickness ) );
				outerShape.lineTo( width / 2 + thickness, height / 2 + thickness );
				outerShape.lineTo( - ( width / 2 + thickness ), height / 2 + thickness );
				outerShape.lineTo( - ( width / 2 + thickness ), - ( height / 2 + thickness ) );

				// Create inner rectangle (hole)
				const innerHole = new THREE.Path();
				innerHole.moveTo( - width / 2, - height / 2 );
				innerHole.lineTo( width / 2, - height / 2 );
				innerHole.lineTo( width / 2, height / 2 );
				innerHole.lineTo( - width / 2, height / 2 );
				innerHole.lineTo( - width / 2, - height / 2 );

				outerShape.holes.push( innerHole );

				const frameGeometry = new THREE.ExtrudeGeometry( outerShape, {
					depth: thickness,
					bevelEnabled: false
				} );

				const frameMesh = new THREE.Mesh( frameGeometry, material );
				frameMesh.position.z = - thickness / 2;
				group.add( frameMesh );

				// Add back plane
				const backGeometry = new THREE.PlaneGeometry( width + ( thickness * 2 ), height + ( thickness * 2 ) );
				const backMesh = new THREE.Mesh( backGeometry, material );
				backMesh.position.set( 0, 0, - thickness / 2 );
				backMesh.rotation.y = Math.PI;
				group.add( backMesh );

				return group;

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				rendererWebGL.setSize( window.innerWidth, window.innerHeight );
				rendererCSS3D.setSize( window.innerWidth, window.innerHeight );

			}

			function animate() {

				controls.update();

				rendererWebGL.render( scene, camera );
				rendererCSS3D.render( scene, camera );

			}

		</script>
	</body>
</html>
